/* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.social.config.support; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.ManagedList; import org.springframework.core.GenericTypeResolver; import org.springframework.social.config.xml.ApiHelper; import org.springframework.social.connect.ConnectionFactory; import org.springframework.social.connect.support.ConnectionFactoryRegistry; import org.springframework.social.security.provider.SocialAuthenticationService; import org.springframework.util.ClassUtils; public abstract class ProviderConfigurationSupport { private final static Log logger = LogFactory.getLog(ProviderConfigurationSupport.class); public ProviderConfigurationSupport(Class<? extends ConnectionFactory<?>> connectionFactoryClass, Class<? extends ApiHelper<?>> apiHelperClass) { this.connectionFactoryClass = connectionFactoryClass; this.apiHelperClass = apiHelperClass; this.apiBindingType = GenericTypeResolver.resolveTypeArgument(connectionFactoryClass, ConnectionFactory.class); if (isSocialSecurityAvailable()) { this.authenticationServiceClass = getAuthenticationServiceClass(); } } protected Class<? extends SocialAuthenticationService<?>> getAuthenticationServiceClass() { return null; } protected static boolean isSocialSecurityAvailable() { try { Class.forName("org.springframework.social.security.SocialAuthenticationServiceLocator"); return true; } catch (ClassNotFoundException cnfe) { return false; } } /** * Creates a BeanDefinition for a provider connection factory. * Although most providers will not need to override this method, it does allow for overriding to address any provider-specific needs. * @param appId The application's App ID * @param appSecret The application's App Secret * @param allAttributes All attributes available on the configuration element. Useful for provider-specific configuration. * @return a BeanDefinition for the provider's connection factory bean. */ protected BeanDefinition getConnectionFactoryBeanDefinition(String appId, String appSecret, Map<String, Object> allAttributes) { return BeanDefinitionBuilder.genericBeanDefinition(connectionFactoryClass).addConstructorArgValue(appId).addConstructorArgValue(appSecret).getBeanDefinition(); } protected BeanDefinition getAuthenticationServiceBeanDefinition(String appId, String appSecret, Map<String, Object> allAttributes) { return BeanDefinitionBuilder.genericBeanDefinition(authenticationServiceClass).addConstructorArgValue(appId).addConstructorArgValue(appSecret).getBeanDefinition(); } protected BeanDefinition registerBeanDefinitions(BeanDefinitionRegistry registry, Map<String, Object> allAttributes) { if (isSocialSecurityAvailable() && authenticationServiceClass != null) { registerAuthenticationServiceBeanDefinitions(registry, allAttributes); } else { registerConnectionFactoryBeanDefinitions(registry, allAttributes); } return registerApiBindingBean(registry, apiHelperClass, apiBindingType,allAttributes); } protected abstract String getAppId(Map<String, Object> allAttributes); protected abstract String getAppSecret(Map<String, Object> allAttributes); private void registerConnectionFactoryBeanDefinitions(BeanDefinitionRegistry registry, Map<String, Object> allAttributes) { BeanDefinition connectionFactoryBD = getConnectionFactoryBeanDefinition(getAppId(allAttributes), getAppSecret(allAttributes), allAttributes); BeanDefinition connectionFactoryLocatorBD = registerConnectionFactoryLocatorBean(registry); registerConnectionFactoryBean(connectionFactoryLocatorBD, connectionFactoryBD, connectionFactoryClass); } @SuppressWarnings("unchecked") private void registerAuthenticationServiceBeanDefinitions(BeanDefinitionRegistry registry, Map<String, Object> allAttributes) { Class<? extends org.springframework.social.security.provider.SocialAuthenticationService<?>> socialAuthenticationServiceClass = (Class<? extends org.springframework.social.security.provider.SocialAuthenticationService<?>>) this.authenticationServiceClass; BeanDefinition authenticationServiceBD = getAuthenticationServiceBeanDefinition(getAppId(allAttributes), getAppSecret(allAttributes), allAttributes); BeanDefinition connectionFactoryLocatorBD = registerConnectionFactoryLocatorBean(registry); registerAuthenticationServiceBean(connectionFactoryLocatorBD, authenticationServiceBD, socialAuthenticationServiceClass); } private BeanDefinition registerConnectionFactoryLocatorBean(BeanDefinitionRegistry registry) { Class<?> connectionFactoryRegistryClass = isSocialSecurityAvailable() ? org.springframework.social.security.SocialAuthenticationServiceRegistry.class : ConnectionFactoryRegistry.class; if (!registry.containsBeanDefinition(CONNECTION_FACTORY_LOCATOR_ID)) { if (logger.isDebugEnabled()) { logger.debug("Registering ConnectionFactoryLocator bean (" + connectionFactoryRegistryClass.getName() + ")"); } BeanDefinitionHolder connFactoryLocatorBeanDefHolder = new BeanDefinitionHolder(BeanDefinitionBuilder.genericBeanDefinition(connectionFactoryRegistryClass).getBeanDefinition(), CONNECTION_FACTORY_LOCATOR_ID); BeanDefinitionHolder scopedProxy = ScopedProxyUtils.createScopedProxy(connFactoryLocatorBeanDefHolder, registry, false); registry.registerBeanDefinition(scopedProxy.getBeanName(), scopedProxy.getBeanDefinition()); } BeanDefinition connectionFactoryLocatorBD = registry.getBeanDefinition(ScopedProxyUtils.getTargetBeanName(CONNECTION_FACTORY_LOCATOR_ID)); return connectionFactoryLocatorBD; } private BeanDefinition registerConnectionFactoryBean(BeanDefinition connectionFactoryLocatorBD, BeanDefinition connectionFactoryBD, Class<? extends ConnectionFactory<?>> connectionFactoryClass) { if (logger.isDebugEnabled()) { logger.debug("Registering ConnectionFactory: " + connectionFactoryClass.getName()); } PropertyValue connectionFactoriesPropertyValue = connectionFactoryLocatorBD.getPropertyValues().getPropertyValue(CONNECTION_FACTORIES); @SuppressWarnings("unchecked") List<BeanDefinition> connectionFactoriesList = connectionFactoriesPropertyValue != null ? (List<BeanDefinition>) connectionFactoriesPropertyValue.getValue() : new ManagedList<BeanDefinition>(); connectionFactoriesList.add(connectionFactoryBD); connectionFactoryLocatorBD.getPropertyValues().addPropertyValue(CONNECTION_FACTORIES, connectionFactoriesList); return connectionFactoryBD; } private BeanDefinition registerAuthenticationServiceBean(BeanDefinition authenticationServiceLocatorBD, BeanDefinition authenticationServiceBD, Class<? extends org.springframework.social.security.provider.SocialAuthenticationService<?>> socialAuthenticationServiceClass) { if (logger.isDebugEnabled()) { logger.debug("Registering SocialAuthenticationService: " + socialAuthenticationServiceClass.getName()); } PropertyValue authenticationServicesPropertyValue = authenticationServiceLocatorBD.getPropertyValues().getPropertyValue(AUTHENTICATION_SERVICES); @SuppressWarnings("unchecked") List<BeanDefinition> authenticationServicesList = authenticationServicesPropertyValue != null ? (List<BeanDefinition>) authenticationServicesPropertyValue.getValue() : new ManagedList<BeanDefinition>(); authenticationServicesList.add(authenticationServiceBD); authenticationServiceLocatorBD.getPropertyValues().addPropertyValue(AUTHENTICATION_SERVICES, authenticationServicesList); return authenticationServiceBD; } private BeanDefinition registerApiBindingBean(BeanDefinitionRegistry registry, Class<? extends ApiHelper<?>> apiHelperClass, Class<?> apiBindingType,Map<String, Object> allAttributes) { if (logger.isDebugEnabled()) { logger.debug("Registering API Helper bean for " + ClassUtils.getShortName(apiBindingType)); } String helperId = "__" + ClassUtils.getShortNameAsProperty(apiBindingType) + "ApiHelper"; // TODO: Make the bean IDs here configurable. BeanDefinition helperBD = getApiHelperBeanDefinitionBuilder(allAttributes).getBeanDefinition(); registry.registerBeanDefinition(helperId, helperBD); if (logger.isDebugEnabled()) { logger.debug("Creating API Binding bean for " + ClassUtils.getShortName(apiBindingType)); } BeanDefinition bindingBD = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); bindingBD.setFactoryBeanName(helperId); bindingBD.setFactoryMethodName("getApi"); bindingBD.setScope("request"); BeanDefinitionHolder scopedProxyBDH = ScopedProxyUtils.createScopedProxy( new BeanDefinitionHolder(bindingBD, ClassUtils.getShortNameAsProperty(apiBindingType)), registry, false); registry.registerBeanDefinition(scopedProxyBDH.getBeanName(), scopedProxyBDH.getBeanDefinition()); return scopedProxyBDH.getBeanDefinition(); } /** * Subclassing hook to allow api helper bean to be configured with attributes from annotation * @param allAttributes additional attributes that may be used when creating the API helper bean. * @return a {@link BeanDefinitionBuilder} for the API Helper */ protected BeanDefinitionBuilder getApiHelperBeanDefinitionBuilder(Map<String, Object> allAttributes) { return BeanDefinitionBuilder.genericBeanDefinition(apiHelperClass) .addConstructorArgReference("usersConnectionRepository") .addConstructorArgReference("userIdSource"); } protected final Class<? extends ConnectionFactory<?>> connectionFactoryClass; protected final Class<? extends ApiHelper<?>> apiHelperClass; protected final Class<?> apiBindingType; protected Class<?> authenticationServiceClass; private static final String CONNECTION_FACTORY_LOCATOR_ID = "connectionFactoryLocator"; private static final String CONNECTION_FACTORIES = "connectionFactories"; private static final String AUTHENTICATION_SERVICES = "authenticationServices"; }